"""
YAML处理工具类，提供YAML文件读取、参数获取等功能，特别为pytest等测试框架设计
"""

try:
    import yaml
    YAML_AVAILABLE = True
except ImportError:
    YAML_AVAILABLE = False
    yaml = None

import os
from typing import Any, Dict, List, Union, Optional


class YamlUtils:
    """
    YAML处理工具类，提供各种YAML操作功能
    """
    
    def __init__(self, yaml_path: str = None, yaml_string: str = None):
        """
        初始化YAML处理工具

        :param yaml_path: YAML文件路径，可选
        :param yaml_string: YAML字符串，可选
        """
        if not YAML_AVAILABLE:
            raise ImportError("PyYAML库未安装，请使用 'pip install PyYAML' 安装")
        
        self.data = {}
        
        if yaml_path:
            self.load_from_file(yaml_path)
        elif yaml_string:
            self.load_from_string(yaml_string)
    
    def load_from_file(self, yaml_path: str) -> None:
        """
        从文件加载YAML

        :param yaml_path: YAML文件路径
        """
        if not os.path.exists(yaml_path):
            raise FileNotFoundError(f"YAML文件不存在: {yaml_path}")
        
        with open(yaml_path, 'r', encoding='utf-8') as file:
            self.data = yaml.safe_load(file) or {}
    
    def load_from_string(self, yaml_string: str) -> None:
        """
        从字符串加载YAML

        :param yaml_string: YAML字符串
        """
        self.data = yaml.safe_load(yaml_string) or {}
    
    def save_to_file(self, yaml_path: str, encoding: str = 'utf-8') -> None:
        """
        保存YAML到文件

        :param yaml_path: 保存路径
        :param encoding: 编码格式
        """
        # 确保目录存在
        directory = os.path.dirname(yaml_path)
        if directory and not os.path.exists(directory):
            os.makedirs(directory)
        
        with open(yaml_path, 'w', encoding=encoding) as file:
            yaml.dump(self.data, file, allow_unicode=True, default_flow_style=False)
    
    def get_data(self) -> Dict[str, Any]:
        """
        获取所有YAML数据

        :return: YAML数据字典
        """
        return self.data
    
    def get_value(self, key_path: str, default: Any = None) -> Any:
        """
        根据路径获取值，支持点分隔的嵌套路径

        :param key_path: 点分隔的键路径，如 "database.host"
        :param default: 默认值
        :return: 对应的值
        """
        keys = key_path.split('.')
        current_data = self.data
        
        try:
            for key in keys:
                if isinstance(current_data, dict) and key in current_data:
                    current_data = current_data[key]
                else:
                    return default
            return current_data
        except (KeyError, TypeError):
            return default
    
    def set_value(self, key_path: str, value: Any) -> None:
        """
        根据路径设置值，支持点分隔的嵌套路径

        :param key_path: 点分隔的键路径，如 "database.host"
        :param value: 要设置的值
        """
        keys = key_path.split('.')
        current_data = self.data
        
        # 遍历到倒数第二个键，确保路径存在
        for key in keys[:-1]:
            if key not in current_data or not isinstance(current_data[key], dict):
                current_data[key] = {}
            current_data = current_data[key]
        
        # 设置最后一个键的值
        current_data[keys[-1]] = value
    
    def delete_key(self, key_path: str) -> bool:
        """
        根据路径删除键

        :param key_path: 点分隔的键路径，如 "database.host"
        :return: 是否删除成功
        """
        keys = key_path.split('.')
        current_data = self.data
        
        # 遍历到倒数第二个键
        for key in keys[:-1]:
            if isinstance(current_data, dict) and key in current_data:
                current_data = current_data[key]
            else:
                return False
        
        # 删除最后一个键
        if isinstance(current_data, dict) and keys[-1] in current_data:
            del current_data[keys[-1]]
            return True
        return False
    
    def has_key(self, key_path: str) -> bool:
        """
        检查键是否存在

        :param key_path: 点分隔的键路径，如 "database.host"
        :return: 键是否存在
        """
        keys = key_path.split('.')
        current_data = self.data
        
        try:
            for key in keys:
                if isinstance(current_data, dict) and key in current_data:
                    current_data = current_data[key]
                else:
                    return False
            return True
        except (KeyError, TypeError):
            return False
    
    def get_keys(self, key_path: str = None) -> List[str]:
        """
        获取指定路径下的所有键

        :param key_path: 点分隔的键路径，如 "database"，如果为None则获取根级别的键
        :return: 键列表
        """
        if key_path is None:
            if isinstance(self.data, dict):
                return list(self.data.keys())
            else:
                return []
        
        keys = key_path.split('.')
        current_data = self.data
        
        try:
            for key in keys:
                if isinstance(current_data, dict) and key in current_data:
                    current_data = current_data[key]
                else:
                    return []
            
            if isinstance(current_data, dict):
                return list(current_data.keys())
            else:
                return []
        except (KeyError, TypeError):
            return []
    
    def get_all_paths(self) -> List[str]:
        """
        获取所有叶子节点的路径

        :return: 所有叶子节点路径列表
        """
        paths = []
        
        def _traverse(data, current_path=""):
            if isinstance(data, dict):
                for key, value in data.items():
                    new_path = f"{current_path}.{key}" if current_path else key
                    if isinstance(value, (dict, list)) and value:
                        _traverse(value, new_path)
                    else:
                        paths.append(new_path)
            elif isinstance(data, list):
                for i, item in enumerate(data):
                    new_path = f"{current_path}[{i}]"
                    if isinstance(item, (dict, list)) and item:
                        _traverse(item, new_path)
                    else:
                        paths.append(new_path)
        
        _traverse(self.data)
        return paths
    
    def merge_yaml(self, other: Union['YamlUtils', Dict[str, Any]]) -> None:
        """
        合并另一个YAML数据

        :param other: 另一个YamlUtils实例或字典
        """
        if isinstance(other, YamlUtils):
            other_data = other.get_data()
        else:
            other_data = other
        
        def _merge_dicts(dict1, dict2):
            for key, value in dict2.items():
                if key in dict1 and isinstance(dict1[key], dict) and isinstance(value, dict):
                    _merge_dicts(dict1[key], value)
                else:
                    dict1[key] = value
        
        _merge_dicts(self.data, other_data)
    
    def to_string(self) -> str:
        """
        将YAML数据转换为字符串

        :return: YAML字符串
        """
        return yaml.dump(self.data, allow_unicode=True, default_flow_style=False)
    
    def get_type(self, key_path: str) -> str:
        """
        获取指定路径值的类型

        :param key_path: 点分隔的键路径
        :return: 值的类型字符串
        """
        value = self.get_value(key_path)
        return type(value).__name__
    
    def get_size(self) -> int:
        """
        获取YAML数据的大小（键值对数量）

        :return: 数据大小
        """
        def _count_items(data):
            count = 0
            if isinstance(data, dict):
                for value in data.values():
                    count += 1
                    if isinstance(value, (dict, list)):
                        count += _count_items(value)
            elif isinstance(data, list):
                for item in data:
                    count += 1
                    if isinstance(item, (dict, list)):
                        count += _count_items(item)
            return count
        
        return _count_items(self.data)
    
    def find_keys_by_value(self, value: Any) -> List[str]:
        """
        根据值查找所有匹配的键路径

        :param value: 要查找的值
        :return: 匹配的键路径列表
        """
        paths = []
        
        def _search(data, current_path="", target_value=value):
            if isinstance(data, dict):
                for key, val in data.items():
                    new_path = f"{current_path}.{key}" if current_path else key
                    if val == target_value:
                        paths.append(new_path)
                    if isinstance(val, (dict, list)):
                        _search(val, new_path, target_value)
            elif isinstance(data, list):
                for i, item in enumerate(data):
                    new_path = f"{current_path}[{i}]"
                    if item == target_value:
                        paths.append(new_path)
                    if isinstance(item, (dict, list)):
                        _search(item, new_path, target_value)
        
        _search(self.data)
        return paths
    
    def update_values(self, updates: Dict[str, Any]) -> None:
        """
        批量更新值

        :param updates: 包含键路径和值的字典
        """
        for key_path, value in updates.items():
            self.set_value(key_path, value)
    
    def filter_by_prefix(self, prefix: str) -> Dict[str, Any]:
        """
        根据键前缀过滤数据

        :param prefix: 键前缀
        :return: 过滤后的数据字典
        """
        result = {}
        
        def _filter(data, current_prefix=""):
            if isinstance(data, dict):
                for key, value in data.items():
                    full_key = f"{current_prefix}.{key}" if current_prefix else key
                    if full_key.startswith(prefix):
                        # 如果当前键匹配前缀，添加到结果中
                        result[full_key] = value
                    elif isinstance(value, dict):
                        _filter(value, full_key)
                    elif isinstance(value, list):
                        for i, item in enumerate(value):
                            if isinstance(item, dict):
                                _filter(item, f"{full_key}[{i}]")
                    elif prefix.startswith(full_key + "."):
                        # 如果前缀以当前键开始，继续深入查找
                        if isinstance(value, dict):
                            _filter(value, full_key)
        
        _filter(self.data)
        return result
    
    @staticmethod
    def from_dict(data: Dict[str, Any]) -> 'YamlUtils':
        """
        从字典创建YamlUtils实例

        :param data: 字典数据
        :return: YamlUtils实例
        """
        yaml_utils = YamlUtils()
        yaml_utils.data = data
        return yaml_utils
    
    @staticmethod
    def create_empty_yaml() -> 'YamlUtils':
        """
        创建空的YAML文档

        :return: YamlUtils实例
        """
        return YamlUtils()
    
    @staticmethod
    def is_valid_yaml(yaml_string: str) -> bool:
        """
        检查字符串是否为有效的YAML格式

        :param yaml_string: YAML字符串
        :return: 是否为有效的YAML
        """
        try:
            yaml.safe_load(yaml_string)
            return True
        except yaml.YAMLError:
            return False